<?php
/**
 *	KLF Framework
 *
 *	Written by Kristian Oye <kristianoye@gmail.com> on Dec 18, 2011
 *	Copyright (C) 2010-2011
 */
namespace KLF\System\Web\MVC
{
	use KLF\KCore;
	use KLF\System\KObject;
	use KLF\System\Errors\KArgumentException;
	
	/**
	 *  Class KControllerActionInvoker
	 *
	 *  Class responsible for finding and constructing actions.
	 *
	 *  @package KLF\System\Web\MVC
	 *  @access public
	 *  @author Kristian Oye <kristianoye@gmail.com>
	 *  @since 0.1
	 *
	 *  @method KControllerActionInvoker NewInstance
	 */
	class KControllerActionInvoker extends KObject implements IActionInvoker
	{
		/**
		 * Invokes a particular action.
		 * 
		 * @param KControllerContext $context
		 */
		public function InvokeAction(KActionContext $context)
		{
			try
			{
				/* @var $method \ReflectionMethod */
				$reflector = new \ReflectionClass($context->Controller);
				$method = $reflector->getMethod($context->ActionName);
				 
				if ($method != null)
				{
					$argNames = $method->getParameters();
					$args = array();
					
					$nc = count($context->RouteData->ValuesByPosition) - count($argNames);
					
					foreach($argNames as $arg)
					{
						/* @var $arg \ReflectionParameter */
						if (isset($context->RouteData->Values[$arg->getName()]))
						{
							$paramValue = $context->RouteData->Values[$arg->getName()];
						}
						elseif ($arg->isDefaultValueAvailable())
						{
							$paramValue = $arg->getDefaultValue();
						}
						elseif ($nc < count($context->RouteData->ValuesByPosition))
						{
							//  Fall back to position if we must
							$paramValue = $context->RouteData->ValuesByPosition->offsetGet($nc++);
						}
						else
						{
							throw new KArgumentException(
								KCore::FS("Action {action} requires parameter {param}",
								array('action' => $actionName, 'param' => $arg->getName())));
						}
						
						$paramValue = urldecode($paramValue);
						
						if (!is_null($paramType = $arg->getClass()))
						{
							try
							{
								$paramValue = $paramType->newInstance($paramValue);
							}
							catch(\Exception $ex)
							{
								throw new KArgumentException(
									KCore::FS("Action parameter '{pname}' must be type '{ptype}'",
									array(
										'pname' => $arg->getName(), 
										'ptype' => $arg->getClass()->getName())));
							}
						}
						array_push($args, $paramValue);
					}
					$result = $method->invokeArgs($context->Controller, $args);
				}
			}
			catch(\Exception $ex)
			{
				print "Oops! " . $ex->getMessage() . "<br/>\n";
				return null;
			}
		}
	}
}
